package service

import (
	"github.com/kordar/k-means-server/src/pojo"
	"github.com/kordar/k-means-server/src/util"
	"sort"
)

type Optics struct {
	Data       []pojo.FeatureItem
	FeatureMap map[string][]float64
	MinPts     int                // 领域密度阈值
	Eps        float64            // 半径
	visited    map[int]int        // 已标记，样本id, 簇id，-1为噪声
	orderList  []pojo.FeatureItem // 结果序列
	reachList  map[int]float64    // 可达距离
	Groups     map[pojo.FeatureItem][]pojo.FeatureItem
	ToMap      map[int]*pojo.FeatureItem // 样本所属分组map
}

func (o *Optics) Run() map[pojo.FeatureItem][]pojo.FeatureItem {
	o.visited = make(map[int]int, len(o.Data))
	o.Groups = make(map[pojo.FeatureItem][]pojo.FeatureItem)
	o.orderList = make([]pojo.FeatureItem, 0, 10)
	o.reachList = make(map[int]float64)
	o.ToMap = make(map[int]*pojo.FeatureItem)
	for _, datum := range o.Data {
		if o.visited[datum.Id] == 0 {
			// 当有标记对象时，随机选取一个对象
			o.execute(datum)
		}
	}

	// log.Println(fmt.Sprintf("%v", o.orderList))
	var current pojo.FeatureItem
	for _, item := range o.orderList {
		if o.reachList[item.Id] == 0 {
			current = item
			o.Groups[current] = make([]pojo.FeatureItem, 0)
		}
		if current != (pojo.FeatureItem{}) {
			o.Groups[current] = append(o.Groups[current], item)
			o.ToMap[item.Id] = &current
		}
	}

	return o.Groups
}

// 邻域（[公式]-neighborhood）
func (o *Optics) neighborhood(corePoint pojo.FeatureItem) []pojo.FeatureItem {
	data := make([]pojo.FeatureItem, 0, o.MinPts)
	// d := make(map[int]float64)
	for _, datum := range o.Data {
		if datum.Id == corePoint.Id {
			continue
		}
		cosine := util.Cosine(o.FeatureMap[corePoint.Url], o.FeatureMap[datum.Url])
		if (1 - cosine) <= o.Eps {
			// d[datum.Id] = 1 - cosine
			data = append(data, datum)
		}
	}

	/*
		sorter := util.NewValSorter(d)
		sorter.Sort()
		if len(sorter.Keys) >= o.MinPts {
			targetKey := sorter.Keys[o.MinPts-1]
			for index, key := range sorter.Keys {
				if index <= o.MinPts-1 {
					d[key] = d[targetKey]
				}
			}
		}
	*/

	return data
}

func (o *Optics) execute(point pojo.FeatureItem) {
	// 标记为visited，并插入结果序列中
	o.visited[point.Id] = point.Id
	o.orderList = append(o.orderList, point)
	// 获取领域
	neighborhood := o.neighborhood(point)
	// log.Println("core = ", point)
	// log.Println("neighborhood = {}", neighborhood)
	// 如果point的领域内至少有M个对象
	if len(neighborhood) >= o.MinPts {
		// 初始化种子列表
		seedList := make([]pojo.FeatureItem, 0)
		// 调用insert_list(), 将领域对象中未被访问的结点按照可达距离插入队列seed_list中
		o.insertList(neighborhood, point, &seedList)
		//log.Println("seedList = {}", seedList)
		if len(seedList) > 0 {
			// 按照可达距离升序取出seedList中第一个元素
			sort.SliceStable(seedList, func(i, j int) bool {
				a := seedList[i].Id
				b := seedList[j].Id
				return o.reachList[a] < o.reachList[b]
			})
			//log.Println("reach = {}", o.reachList)
			//log.Println("升序排序 = {}", seedList)
			for {
				if len(seedList) == 0 {
					return
				}
				first := seedList[0]
				//log.Println("first = ", first)
				seedList = seedList[1:]
				// 标记该元素为visited，插入结果序列order_list中
				o.visited[first.Id] = first.Id
				o.orderList = append(o.orderList, first)
				// 获取领域
				neighborhood2 := o.neighborhood(first)
				// 如果point的领域内至少有M个对象
				if len(neighborhood2) >= o.MinPts {
					o.insertList(neighborhood2, first, &seedList)
					//log.Println("领域2 = ", seedList)
				}
			}

		}
	}

}

func (o *Optics) rd(k pojo.FeatureItem, core pojo.FeatureItem) float64 {
	return 1 - util.Cosine(o.FeatureMap[core.Url], o.FeatureMap[k.Url])
}

func (o *Optics) insertList(k []pojo.FeatureItem, core pojo.FeatureItem, seedList *[]pojo.FeatureItem) {
	// 对i中所有的领域点k
	for _, item := range k {
		// 如果k未被访问过
		if o.visited[item.Id] == 0 {
			rd := o.rd(item, core)
			if o.reachList[item.Id] == 0 {
				o.reachList[item.Id] = rd
				*seedList = append(*seedList, item)
			} else if o.reachList[item.Id] > rd {
				o.reachList[item.Id] = rd
			}
		}
	}
}
